using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using OpenLS.Spreadsheet.UI.Internals;

namespace OpenLS.Spreadsheet.UI
{
    internal class ClipboardMonitor : Window
    {
        private readonly DependencyObject _target;


        public static ClipboardMonitor GetMonitor(DependencyObject obj)
        {
            return (ClipboardMonitor)obj.GetValue(MonitorProperty);
        }

        public static void SetMonitor(DependencyObject obj, ClipboardMonitor value)
        {
            obj.SetValue(MonitorProperty, value);
        }

        // Using a DependencyProperty as the backing store for Monitor.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MonitorProperty =
            DependencyProperty.RegisterAttached("Monitor", typeof(ClipboardMonitor), typeof(ClipboardMonitor), new UIPropertyMetadata(null));
        
        internal static void EnsureMonitored (DependencyObject element2)
        {
            DependencyObject element = EditionElement.GetEditorRoot(element2);

            var monitor = GetMonitor(element);
            if (monitor != null)
                return;
            monitor = new ClipboardMonitor(element);
            SetMonitor(element, monitor);
            monitor.ClipboardChanged += new EventHandler(MonitorClipboardChanged);
        }
        internal static void ClearMonitor(DependencyObject element)
        {
            var monitor = GetMonitor(element);
            if (monitor == null)
                return;
            monitor.Close();
            SetMonitor(element, null);
        }
        static void MonitorClipboardChanged(object sender, EventArgs e)
        {
            ClipboardMonitor monitor = (ClipboardMonitor) sender;
            SetMonitor(monitor._target, null);
            SpreadsheetElement.SetHighlightFor(monitor._target, null);
            monitor.Close();
        }
        
        private const int WM_CHANGECBCHAIN = 781;
        private const int WM_DRAWCLIPBOARD = 776;
        private IntPtr _nextHwnd;

        public ClipboardMonitor(DependencyObject target)
        {
            _target = target;
            Loaded += ClipboardMonitorLoaded;
            this.Visibility = Visibility.Hidden;
            this.Opacity = 0;
            this.WindowStyle = WindowStyle.None;
            this.AllowsTransparency = true;
            this.Background = null;
            this.ResizeMode = ResizeMode.NoResize;
            
            Show();
        }
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            var h = new WindowInteropHelper(this);
            HwndSource source = HwndSource.FromHwnd(h.Handle);
            ChangeClipboardChain(source.Handle, _nextHwnd);
        }

        [DllImport("user32.dll")]
        private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        private void ClipboardMonitorLoaded(object sender, RoutedEventArgs e)
        {
            var h = new WindowInteropHelper(this);
            HwndSource source = HwndSource.FromHwnd(h.Handle);
            source.AddHook(OnMessage);
            _nextHwnd = SetClipboardViewer(h.Handle);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        private IntPtr OnMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
        {
            switch (msg)
            {
                case WM_DRAWCLIPBOARD:
                    EventHandler h = ClipboardChanged;
                    if (h != null)
                        h(this, EventArgs.Empty);

                    handled = true;
                    break;
                case WM_CHANGECBCHAIN:
                    if (wparam == _nextHwnd)
                    {
                        _nextHwnd = lparam;
                    }
                    else
                    {
                        SendMessage(_nextHwnd, msg, wparam, lparam);
                    }
                    break;
            }
            return IntPtr.Zero;
        }

        public event EventHandler ClipboardChanged;

        [DllImport("user32.dll")]
        private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
    }
}